import java.util.Scanner;

/**
 * This is a game board class. Every game is a new instance of the GameBoard
 * @author Andrei Kashin
 *
 */
public class GameBoard {
	
	// Scanner for user input
	Scanner userInput = new Scanner(System.in);
	
	// class fields
	private static String gameBoardFrame = "" +
			"+---+---+---+\n" +
			"|   |   |   |\n" +
			"+---+---+---+\n" +
			"|   |   |   |\n" +
			"+---+---+---+\n" +
			"|   |   |   |\n" +
			"+---+---+---+";
	private char[] gameBoardChars;
	private int[] elements = {0, 0, 0, 0, 0, 0, 0, 0, 0};
	private int[] locations = {16, 20, 24, 44, 48, 52, 72, 76, 80};
	private int[] pointHelper = {8, 1, 6, 3, 5, 7, 4, 9, 2};
	private boolean gameOver;
	
	// class constructor
	/**
	 * Game board constructor.
	 */
	GameBoard() {
		gameOver = false;
		gameBoardChars = new char[gameBoardFrame.length()];
		for (int i = 0; i < gameBoardFrame.length(); i++){
			gameBoardChars[i] = gameBoardFrame.charAt(i);
		}
	}	
	
	// Class methods:
	// human turn
	/**
	 * Human turn is a void method. Prints X to the field corresponding
	 * to the user input.
	 * <p>
	 * Method also checks if field is not empty. Prints warning message whenever user or computer
	 * already used given field. Lets user to re-enter the field number.
	 */
	public void HumanTurn() {
		while (true) {
			int fieldNumber = PromptField() - 1;
			
			// exit game if user input 0
			if (fieldNumber == -1) {
				gameOver = true;
				return;
			}
			
			// if field is empty - set to 1
			if (elements[fieldNumber] == 0) {
				elements[fieldNumber] = 1;
				return;
			}
			else if (elements[fieldNumber] == 1) {
				System.out.println("You have already put here");
			}
			else if (elements[fieldNumber] == -1) {
				System.out.println("Computer has already put here");
			}
		}
	}
	
	// computer turn
	/**
	 * Computer turn is a void method. Computer checks the game board and inserts 'O' to the
	 * empty field according to game algorithm.
	 */
	public void CompTurn() {
		
		// try to put to center
		if (elements[4] == 0){ // 4 is center
			elements[4] = -1;
			return;
		}
		
		// try to put to corners
		else if (elements[0] == 0 ||
				elements[2] == 0 ||
				elements[6] == 0 ||
				elements[8] == 0) {  // 0, 2, 6, 8 are corners
			while (true) {
				
				// choose random corner
				int rand = (int)(Math.random()*4);
				int[] corners = {0, 2, 6, 8};
				if (elements[corners[rand]] == 0) {
					elements[corners[rand]] = -1;
					return;
				}
			}
		}
		
		// if center and corners are not empty, 
		// put to any random empty field from the rest
		else if (elements[1] == 0 ||
				elements[3] == 0 ||
				elements[5] == 0 ||
				elements[7] == 0) {  // 1, 3, 5, 7 are rest
			while (true) {
				int rand = (int)(Math.random()*4);
				int[] rest = {1, 3, 5, 7};
				if (elements[rest[rand]] == 0) {
					elements[rest[rand]] = -1;
					return;
				}
			}
		}
		else {
			// if there is no empty fields -> return
			return;
		}
	}
	
	// method user prompt
	/**
	 * Method ask user for input a number until user puts a number in interval 0-9.
	 * @return the field number in interval 0-9, which user has inserted.
	 */
	public int PromptField() {
		System.out.println("Enter a number between 1-9. 0 - exit.");
		while (!userInput.hasNextInt()) {
			System.out.println("Your input has to be 1 - 9. 0 - exit.");
			userInput.next();
		}
		int fieldNumber = userInput.nextInt();
		while (fieldNumber < 0 || fieldNumber > 9) {
			System.out.println("Your input has to be 1 - 9 0 - exit.");
			fieldNumber = userInput.nextInt();
		}
		return fieldNumber;
	}
	
	// method which draws game board
	/**
	 * Method draws a game board using pseudo-graphics. If there is no any empty field then method ends the game.
	 */
	public void DrawBoard() {
		for (int i = 0; i < elements.length; i++) {
			if (elements[i] == 0){
				gameBoardChars[locations[i]] = ' '; // if element 0 then type space 
			}
			else if (elements[i] == 1) {
				gameBoardChars[locations[i]] = 'X'; // if element 1 then type X
			}
			else if (elements[i] == -1) {
				gameBoardChars[locations[i]] = 'O'; // if element -1 then type O
			}
		}
		
		// check for empty fields
		int count = 0;
		for (int i = 0; i < elements.length; i++) {
			if (elements[i] != 0) {
				count++;
			}
		}
		
		// if there is no any empty field
		if (count == elements.length) {
			gameOver = true;
			System.out.println(gameBoardChars);
			return;
		}
		else {
			// print out board
			System.out.println(gameBoardChars);
		}		
	}
	
	// method check who is winner
	/**
	 * Winner calculation performed using magic square 3x3.
	 * <p>
	 * Method checks every row, column and diagonals. If sum in any row, column or diagonal is equal to 15,
	 * then user won. If sum is equal to negative 15, then computer is winner.
	 * @return the winner, or empty string if there is no winner yet.
	 */
	public String CheckWinner() {
		int[] e = elements;
		int[] p = pointHelper;
		
		// check every row. if sum=15 or -15 return winner
		for (int i = 0; i < e.length; i+=3) {
			if (e[i] * p[i] + e[i+1] * p[i+1] + e[i+2] * p[i+2] == 15) {
				gameOver = true;
				return "Human";
			}
			else if (e[i] * p[i] + e[i+1] * p[i+1] + e[i+2] * p[i+2] == -15) {
				gameOver = true;
				return "Computer";
			}
		}
		
		// check every column. if sum=15 or -15 return winner
		for (int i = 0; i < e.length/3; i++) {
			if (e[i] * p[i] + e[i+3] * p[i+3] + e[i+6] * p[i+6] == 15) {
				gameOver = true;
				return "Human";
			}
			else if (e[i] * p[i] + e[i+3] * p[i+3] + e[i+6] * p[i+6] == -15) {
				gameOver = true;
				return "Computer";
			}
		}
		
		// check cross. if 15 or -15, return winner
		for (int i = 0; i < 2; i++) {
			if (e[0] * p[0] + e[4] * p[4] + e[8] * p[8] == 15 ||
				e[2] * p[2] + e[4] * p[4] + e[6] * p[6] == 15) {
				gameOver = true;
				return "Human";
			}
			else if (e[0] * p[0] + e[4] * p[4] + e[8] * p[8] == -15 ||
					 e[2] * p[2] + e[4] * p[4] + e[6] * p[6] == -15) {
				gameOver = true;
				return "Computer";
			}
		}
		
		// empty string otherwise
		return "";
	}
	
	// game over get method
	/**
	 * Method returns true if game is over, false otherwise.
	 * @return a game over state
	 */
	public boolean IsGameOver() {
		return gameOver;
	}
}